
use std::ptr;

use serde::Deserialize;

use crate::{error::{new_err, AppErr}, utils::{codec::{encode_u16, encode_u8}, new_buf}};



pub const HEAD: u16 = 0xE11E;
pub const MIN_LEN: usize = 6;

pub const TYPE_PING: u8 = 0;
pub const TYPE_PONG: u8 = 1;
pub const TYPE_ACK: u8 = 2;
pub const TYPE_REQ: u8 = 3;
pub const TYPE_RES: u8 = 4;
pub const TYPE_NOTIFY: u8 = 5;
pub const TYPE_NOTIFY_ACK: u8 = 6;

pub const CMD_LOGIN: u8 = 0x00;

pub struct RecvFrame {
    buf: Box<[u8]>,
}

impl RecvFrame {

    pub fn new(buf: Box<[u8]>) -> Self {
        Self { buf }
    }

    pub fn ctype(&self) -> u8 {
        self.buf[0]
    }

    pub fn seq(&self) -> u8 {
        self.buf[1]
    }

    pub fn id(&self) -> u16 {
        ((self.ctype() as u16) << 8) +
        (self.seq() as u16)
    }

    pub fn cmd(&self) -> Result<u8, AppErr> {
        if self.buf.len() < 3 {
            return Err(new_err("cmd len < 3"));
        }
        Ok(self.buf[2])
    }

    pub fn body(&self) -> Result<&[u8], AppErr> {
        if self.buf.len() < 4 {
            return Err(new_err("body len < 4"));
        }
        Ok(&self.buf[3..])
    }

    pub fn parse<'a, T: Deserialize<'a>>(&'a self) -> Result<T, AppErr> {
        if self.buf.len() < 4 {
            return Err(new_err("parse cbor fail"));
        }
        let v = serde_cbor::from_slice(&self.buf[3..])?;
        Ok(v)
    }

}


pub fn make_empty(ctype: u8, seq: u8) -> [u8; MIN_LEN] {

    let mut buf = [0u8; MIN_LEN];
    encode_u16(&mut buf, HEAD);
    encode_u16(&mut buf[2..], MIN_LEN as u16);
    encode_u8(&mut buf[4..], ctype);
    encode_u8(&mut buf[5..], seq);
    buf
}

pub fn make(ctype: u8, seq: u8, cmd: u8, args: &[u8]) -> Box<[u8]> {
    let len = MIN_LEN + 1 + args.len();
    let mut buf = new_buf(len);
    encode_u16(&mut buf, HEAD);
    encode_u16(&mut buf[2..], len as u16);
    encode_u8(&mut buf[4..], ctype);
    encode_u8(&mut buf[5..], seq);

    encode_u8(&mut buf[6..], cmd);
    unsafe {
        let dst = &mut buf[7..];
        ptr::copy_nonoverlapping(args.as_ptr(), dst.as_mut_ptr(), args.len());
    }
    buf
}

